/**
 * Copyright (c) 2007,中企动力华南研发中心<br>
 * All rights reserved.
 */
package com.ce.pms.charge.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.ce.pms.base.service.impl.PmsBaseServiceImpl;
import com.ce.pms.charge.dao.IArCalculateDao;
import com.ce.pms.charge.dao.IBusinessCodeDao;
import com.ce.pms.charge.dao.ICharStoChoGeneralDao;
import com.ce.pms.charge.dao.IMeterDataRegisterDao;
import com.ce.pms.charge.dao.IMeterVersionDao;
import com.ce.pms.charge.dao.IPeriodDao;
import com.ce.pms.charge.dao.ISubAccountDao;
import com.ce.pms.charge.dao.ITransactionDao;
import com.ce.pms.charge.model.ArCalculateModel;
import com.ce.pms.charge.model.DebitTransDomain;
import com.ce.pms.charge.model.MeterVersion;
import com.ce.pms.charge.service.IArRecalculateService;
import com.ce.pms.common.model.SysrefcodeDO;
import com.ce.pms.exception.BusinessException;
import com.ce.pms.house.dao.IHouseDao;
import com.ce.pms.house.model.HouseDomain;

/**
 * 功能描述：应收计算的重算<br>
 * @author 罗军林 创建日期：2008-9-12
 */
public class ArRecalculateServiceImpl extends PmsBaseServiceImpl implements
		IArRecalculateService {
	IArCalculateDao arCalculateDao;
	IMeterVersionDao meterVersionDao;

	private IHouseDao houseDao;
	private IBusinessCodeDao businessCodeDao;
	private ISubAccountDao subAccountDao;
	private IPeriodDao periodDao;
	private ICharStoChoGeneralDao charStoChoGeneralDao;
	private IMeterDataRegisterDao meterDataRegisterDao;
	private ITransactionDao transactionDao;

	public void reCalculate(Long userId, Long periodId,String houseIds, String chargetype) {
		checkHouseHasCharStoCho(houseIds, chargetype);

		checkHouseAccountLockState(houseIds);

		checkHouseTransactionLocked(houseIds,periodId);

		checkHouseHasMeterDatas(periodId, houseIds, chargetype);

		deleteOldTran(periodId, houseIds, chargetype);

		List<ArCalculateModel> houseList = getHouseList(houseIds, periodId, chargetype);
		checkHouseList(houseList);

		if(SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_METER.equals(chargetype)){
			List<Object[]> datasRTransaction=new ArrayList<Object[]>();//所有产生的交易ID，及原始数据ID
			List<String> datas=new ArrayList<String>();//原始数据集合

			Iterator<ArCalculateModel> iter = houseList.iterator();
			while (iter.hasNext()) {
				ArCalculateModel arcalculateModel = iter.next();
				DebitTransDomain debitTran=saveDebitTrans(userId, datasRTransaction, arcalculateModel);

				addDatas(datas, arcalculateModel.getChargeDatasIds());
				if (debitTran==null) {
					iter.remove();
					continue;
				}
				arcalculateModel.setAmt(debitTran.getDebit());
			}

			updateBatchDatasStatus(datas);
			arCalculateDao.addBatchDatasRTransaction(datasRTransaction);
		}else{
			arCalculateDao.insertTransaction(wrapHouseList(houseList,userId));
		}

		updateSubAccountBalance(countSubAccountBalance(houseList));

		arCalculateDao.updateMtranId();
	}

	/**
	 * 功能描述:删除旧的交易,如果是走表费用，交易不删除<br>
	 * @param periodId
	 * @param houseIds
	 * @param chargetype<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-30<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void deleteOldTran(Long periodId, String houseIds, String chargetype) {
		String tranIds=arCalculateDao.queryReCalculateTran(houseIds, periodId,chargetype);
		if( tranIds!=null && tranIds.trim().length()>0 && !SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_METER.equals(chargetype)){
				arCalculateDao.updateSubAccountBalanceSub(packParameter(arCalculateDao.querySumDebitBySubAccountId(periodId,tranIds)));
				arCalculateDao.deleteTransaction(periodId,tranIds);
		}
	}

	/**
	 * 功能描述:将本次参与计算的原始数据添回至缓存<br>
	 * @param datas
	 * @param chargeDatasIds<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-29<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void addDatas(List<String> datas, String chargeDatasIds) {
		String[] datars=chargeDatasIds.split(",");
		for(String dataID : datars){
			datas.add(dataID);
		}
	}

	/**
	 * 功能描述:检测指定房号ID列表中物业帐户的借方状态，如果有被锁定将抛出异常<br>
	 * @param houseIds<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-23<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void checkHouseAccountLockState(String houseIds) {
		String houseNumbers=subAccountDao.queryLockDdebitChargeSubAccount(houseIds);
		if(null!=houseNumbers && houseNumbers.trim().length()>0){
			throw new BusinessException("charge:chargeAccount_is_recalculate,"+houseNumbers,BusinessException.WARNING);
		}
	}

	/**
	 * 功能描述:检测指定的房间指定周期是否有出盘锁定状态的交易，如果有被锁定将抛出异常<br>
	 * @param houseIds<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-23<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void checkHouseTransactionLocked(String houseIds,Long periodId) {
		if(null==houseIds)throw new BusinessException("charge:houseId_not_null",BusinessException.ERROR);
		String[] hids= houseIds.split(",");
		StringBuffer houseNumbers=new StringBuffer();
		for(int i=0;i<hids.length;i++){
			Boolean result=transactionDao.isHouseTransactionLocked(new Long(hids[i]), periodId);
			if(result){
				HouseDomain house= houseDao.findById(new Long(hids[i]));
				houseNumbers.append(house.getHouseNumber());
				if(i<hids.length-1){
					houseNumbers.append(",");
				}
			}
		}
		if(houseNumbers.length()>0){
			throw new BusinessException("charge:house_has_transaction_locked,"+houseNumbers.toString(),BusinessException.ERROR);
		}
	}

	/**
	 * 功能描述:<br>
	 * @param houseList<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-16<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void checkHouseList(List<ArCalculateModel> houseList) {
		if(null==houseList || houseList.size()<=0){
			throw new BusinessException("charge:recalculate_fail",BusinessException.ERROR);
		}
	}

	/**
	 * 功能描述:如果重算类型是走表，则检查是否有房间是否有新建状态的原始数据<br>
	 * @param periodId
	 * @param houseIds
	 * @param chargetype<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-13<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void checkHouseHasMeterDatas(Long periodId, String houseIds, String chargetype) {
		if(SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_METER.equals(chargetype)){
			if(null==houseIds)throw new BusinessException("charge:houseId_not_null",BusinessException.ERROR);
			String[] hids= houseIds.split(",");
			StringBuffer houseNumbers=new StringBuffer();
			for(int i=0;i<hids.length;i++){
				List result=meterDataRegisterDao.getNewMeterDatasListByPeriodId(periodId,new Long(hids[i]));
				if(null==result || result.size()<1){
					HouseDomain house= houseDao.findById(new Long(hids[i]));
					houseNumbers.append(house.getHouseNumber());
					if(i<hids.length-1){
						houseNumbers.append(",");
					}
				}
			}
			if(houseNumbers.length()>0){
				throw new BusinessException("charge:house_has_no_newMeterData,"+houseNumbers.toString(),BusinessException.ERROR);
			}
		}
	}

	/**
	 * 功能描述:检查指定的房间是否都选用了标准<br>
	 * @param houseIds
	 * @param chargetype<br>
	 * Create author:罗军林<br>
	 * Create on:2008-10-10<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void checkHouseHasCharStoCho(String houseIds, String chargetype) {
		if(null==houseIds)throw new BusinessException("charge:houseId_not_null",BusinessException.ERROR);
		String[] hids= houseIds.split(",");
		StringBuffer houseNumbers=new StringBuffer();
		for(int i=0;i<hids.length;i++){
			boolean result=charStoChoGeneralDao.checkExistsCharStaCho(chargetype, new Long(hids[i]));
			if(!result){
				HouseDomain house= houseDao.findById(new Long(hids[i]));
				houseNumbers.append(house.getHouseNumber());
				if(i<hids.length-1){
					houseNumbers.append(",");
				}
			}
		}
		if(houseNumbers.length()>0){
			throw new BusinessException("charge:house_has_no_charsta,"+houseNumbers.toString(),BusinessException.ERROR);
		}
	}

	/**
	 * 功能描述:<br>
	 * @param userId
	 * @param datasRTransaction
	 * @param arcalculateModel<br>
	 * Create author:罗军林<br>
	 * Create on:2008-9-17<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private DebitTransDomain saveDebitTrans(Long userId, List<Object[]> datasRTransaction, ArCalculateModel arcalculateModel) {
		MeterVersion meterVersion=meterVersionDao.findById(arcalculateModel.getChargeStaVerionId());
		BigDecimal amt=meterVersion.calculate(arcalculateModel.getWastage());
		if(amt==null || BigDecimal.ZERO.compareTo(amt)>=0){
			return null;
		}
		DebitTransDomain debitTransDomain=new DebitTransDomain();
		debitTransDomain.setHouse(houseDao.findById( arcalculateModel.getHouseId() ));
		debitTransDomain.setDebit(amt);
		debitTransDomain.setBusinessCode(businessCodeDao.findById(arcalculateModel.getBizCodeId()));
		debitTransDomain.setStaVersion(meterVersion);
		debitTransDomain.setBindingAccount(subAccountDao.findById( arcalculateModel.getSubAccountId() ));
		debitTransDomain.setPeriod(periodDao.findById(arcalculateModel.getPeriodId()));
		debitTransDomain.setClazz(SysrefcodeDO.FEE_TRANSACTION_CLAZZ_PRINCIPAL);
		debitTransDomain.setStatuz(SysrefcodeDO.FEE_TRANSACTION_STATUZ_NOTPAY);
		debitTransDomain.setSysInfo(this.createInfo(userId));
		arCalculateDao.saveObject(debitTransDomain);

		String[] datars=arcalculateModel.getChargeDatasIds().split(",");
		for(String dataID : datars){
			datasRTransaction.add(new Object[]{dataID,debitTransDomain.getTransactionId()});
		}
		return debitTransDomain;
	}

	/**
	 * 功能描述:更新原始数据状态为已计算<br>
	 * @param datasRTransaction<br>
	 * Create author:罗军林<br>
	 * Create on:2008-9-17<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private void updateBatchDatasStatus(List<String> datas){
		List<Object []> datasIds=new ArrayList<Object[]>();
		for(String item : datas){
			datasIds.add(new Object[]{SysrefcodeDO.FEE_CHARGE_DATAS_STATUS_COUNTED,item});
		}
		arCalculateDao.updateBatchDatasStatus(datasIds);
		logger.info("原始数据状态更新成功。更新了"+datasIds.size()+"条");
	}

	/**
	 * 功能描述:<br>
	 * @param houseIds
	 * @param chargetype
	 * @return<br>
	 * Create author:罗军林<br>
	 * Create on:2008-9-16<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private List<ArCalculateModel> getHouseList(String houseIds, Long periodId, String chargetype) {
		List<ArCalculateModel> houseList=null;

		if(SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_GENERAL.equals(chargetype)){
			houseList=arCalculateDao.queryGeneralByHouseIds(houseIds,periodId);
		}else if(SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_FIXED.equals(chargetype)){
			houseList=arCalculateDao.queryFixedByHouseIds(houseIds,periodId);
		}else if(SysrefcodeDO.FEE_CHAR_TYPE_CLASS_CODE_METER.equals(chargetype)){
			houseList=arCalculateDao.queryMeterByHouseIds(houseIds,periodId);
		}else{
			throw new BusinessException("charge:charge_not_type",BusinessException.ERROR);
		}
		return houseList;
	}

	private void updateSubAccountBalance(Map<Long,BigDecimal> subAccountBalances) {
		List<Object[]> parameterList=new ArrayList<Object[]>();
		for(Long subAccountId : subAccountBalances.keySet()){
			Object[] parameter=new Object[]{subAccountBalances.get(subAccountId),subAccountId};
			parameterList.add(parameter);
		}
		arCalculateDao.updateSubAccountBalance(parameterList);
		subAccountBalances.clear();
	}


	private Map<Long,BigDecimal> countSubAccountBalance(List<ArCalculateModel> houseList){
		Map<Long,BigDecimal> subAccount=new HashMap<Long, BigDecimal>();
		for(ArCalculateModel houseItem : houseList){
			BigDecimal balance= subAccount.get(houseItem.getSubAccountId());
			if(balance==null){
				subAccount.put(houseItem.getSubAccountId(), houseItem.getAmt());
			}else{
				subAccount.put(houseItem.getSubAccountId(), addBigDecimal(houseItem.getAmt(),balance));
			}
		}
		return subAccount;
	}

	/**
	 * 功能描述:返回两数的和，如果有一项为NULL，则直接返回另一项<br>
	 * @param a
	 * @param b
	 * @return<br>
	 * Create author:罗军林<br>
	 * Create on:2008-9-24<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private BigDecimal addBigDecimal(BigDecimal a,BigDecimal b){
		if(null==a){
			return b;
		}else if(null==b){
			return a;
		}else{
			return a.add(b);
		}
	}

	private List<Object[]> wrapHouseList(List<ArCalculateModel> houseList,Long userId){
		List<Object[]> parameterList=new ArrayList<Object[]>();

		for(ArCalculateModel houseItem : houseList){
			if(houseItem.getAmt()==null || BigDecimal.ZERO.compareTo(houseItem.getAmt())>=0){
				continue;
			}
			Object[] parameter={
					houseItem.getHouseId(),
					houseItem.getAmt(),
					houseItem.getBizCodeId(),
					houseItem.getChargeStaVerionId(),
					houseItem.getSubAccountId(),
					SysrefcodeDO.FEE_TRANSACTION_CLAZZ_PRINCIPAL,
					SysrefcodeDO.FEE_TRANSACTION_STATUZ_NOTPAY,
					houseItem.getPeriodId(),
					userId
			};
			parameterList.add(parameter);
		}
		return parameterList;
	}

	/**
	 * 功能描述:<br>
	 * @param subAccount
	 * @return<br>
	 * Create author:罗军林<br>
	 * Create on:2008-9-12<br>
	 * Edit author:<br>
	 * Edit on:<br>
	 * Why:<br>
	 */
	private List<Object[]> packParameter(Map<Long, BigDecimal> subAccount) {
		List<Object[]> parameterList=new ArrayList<Object[]>();
		for(Long subAccountId : subAccount.keySet()){
			parameterList.add(new Object[]{subAccount.get(subAccountId),subAccountId});
		}
		return parameterList;
	}

	public void setArCalculateDao(IArCalculateDao arCalculateDao) {
		this.arCalculateDao = arCalculateDao;
	}

	public void setBusinessCodeDao(IBusinessCodeDao businessCodeDao) {
		this.businessCodeDao = businessCodeDao;
	}

	public void setHouseDao(IHouseDao houseDao) {
		this.houseDao = houseDao;
	}

	public void setMeterVersionDao(IMeterVersionDao meterVersionDao) {
		this.meterVersionDao = meterVersionDao;
	}

	public void setPeriodDao(IPeriodDao periodDao) {
		this.periodDao = periodDao;
	}

	public void setSubAccountDao(ISubAccountDao subAccountDao) {
		this.subAccountDao = subAccountDao;
	}

	public void setCharStoChoGeneralDao(ICharStoChoGeneralDao charStoChoGeneralDao) {
		this.charStoChoGeneralDao = charStoChoGeneralDao;
	}

	public void setMeterDataRegisterDao(IMeterDataRegisterDao meterDataRegisterDao) {
		this.meterDataRegisterDao = meterDataRegisterDao;
	}

	public void setTransactionDao(ITransactionDao transactionDao) {
		this.transactionDao = transactionDao;
	}

}
